﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EmotionRecognitionEntities;
using EmotionRecognitionDB;

namespace EmotionRecognitionManager
{
    public class Retrieval
    {

        public Retrieval() { }//


        #region Step 1 - Creation of containers for process variables
        //Step 1 in the algorithm- Creation of lists
        //Solution
        //Chunk --   And All cases that belongs to the chunk that retrievd by Retrieval algorithm
        Dictionary<Chunk, Case> solutions = new Dictionary<Chunk, Case>();
        //List for Saving InputAUList
        List<int> savedAuList = new List<int>();
        //InputAUlist
        List<int> inputAuList = new List<int>();
        //will hold All cases of all chunks - wil be filled after step 2
        List<Case> casesList = new List<Case>();
        // will be used in Step 2
        List<Case> IndexcasesList = new List<Case>();
        //BestCases in Step 3
        List<Case> BestCases = new List<Case>();
        //chunks Id list
        List<int> chunkId = new List<int>();
        //Full chunks list
        List<Chunk> chunks = new List<Chunk>(); // contains the labels inside
        #endregion

        #region Run retrieval algorithm

        public ReplyMsg ExecuteRetrievalAlgoritm(List<int> auList)
        {
            inputAuList = auList;

            SaveAuList();
            //Step 1- in the algorithm
            SetCasesList();
            //Step 2 - in the algorithm
            CreateBestCases();
            //Step 3 - Solutions Dictionary will be filled 
            MatchBestCasesWithAulist();
            return new ReplyMsg(true, solutions);

        }

        #endregion

        #region Step 2 - methods
        /// <summary>
        /// set Case List
        /// 1. will fill casesList - a  ll cases of chunks that we got from index cases
        /// </summary>
        /// <param name="auList"></param>
        private void SetCasesList()
        {

            /*
             * Check which of given cases in casesIDList is a index 
             * 
             *  fill IndexcasesList with IndexCases
             * 
             *  Index Caes
             */

            IndexcasesList = new List<Case>();
            DBQueries.GetIndexCases(IndexcasesList);
            //filter all of the irrelevant index case
            MatchInputAuToIndexCases();
            //recieve all cases of given chunks
            casesList = new List<Case>();
            AddAllCaseOfchunktoCaseList();
            chunks = new List<Chunk>();
            CreateChunks();
            //GetchunksToClusters();


            //step 1 is over - caselist is filled with SelecctedCases 

        }
        /// <summary>
        /// //Save Aulist in List<int> - for later use in step 4
        /// </summary>
        private void SaveAuList()
        {

            foreach (int au in inputAuList)
            {
                savedAuList.Add(au);

            }


        }
        /// <summary>
        /// example is shown in  function
        /// for each index case  - check if input list  au are present in indexcase 	

        /// </summary>
        private void MatchInputAuToIndexCases()
        {


            // foreach case Index
            // check if case index is completely inside input au list
            // if so discard aus belong to case index from input au list
            // then continue the rest of the case indices

            for (int i = 0; i < IndexcasesList.Count; i++)
            {

                bool keepCase = true;
                //for each au in Index Case
                foreach (int au in IndexcasesList[i].AuList)
                {
                    // if at least one au dont apears in both case index and input au list
                    // discard case -> keepCase == false
                    if (inputAuList.Contains(au) == false)
                        keepCase = false;
                }


                if (keepCase == false)
                { // removes the case index from case index
                    IndexcasesList.RemoveAt(i);
                    i--;
                }
            }



        }
        /// <summary>
        /// get chunks to clusters List<chunk>
        /// 
        /// </summary>
        private void CreateChunks()
        {
            foreach (int id in chunkId)
            {
                //get chunk from DB
                Chunk chunk = DBQueries.GetchunkByChunkID(id);
                if (chunk != null)
                {
                    //add chunk to clusters
                    chunks.Add(chunk);
                }

            }

        }

        /// <summary>
        /// Add all cases of selected chunks to caselist
        /// </summary>
        private void AddAllCaseOfchunktoCaseList()
        {

            chunkId = new List<int>();
            foreach (Case caseClass in IndexcasesList)
            {
                if (chunkId.Contains(caseClass.ChunkID) == false)
                    chunkId.Add(caseClass.ChunkID);
            }

            foreach (int chunk in chunkId)
            {
                DBQueries.GetCasesByChunkID(chunk, casesList);
            }


        }



        #endregion

        #region Step 3 - methods
        /// <summary>
        /// Create Best Cases-
        /// 1. create best cases from caseList
        /// 2. example inside method
        /// </summary>
        private void CreateBestCases()
        {
            for (int i = 0; i < casesList.Count; i++)
            {
                //assume that that case is composed from InputList
                bool IsComposed = true;

                /*
                 * for example -  Case 1 -[AU1,AU2,AU5]  case 2 - [AU4,AU5]
                 * InputList - {AU1,AU2,AU5]
                 * Case 1 will be add to Best cases , becasue all his AU's is present in InputList
                 */
                //for each au in caseList
                foreach (int au in casesList[i].AuList)
                {
                    // if input List  not conatain Case AU, 
                    if (savedAuList.Contains(au) == false)
                    {
                        IsComposed = false;
                    }

                }

                if (IsComposed == true)
                {
                    BestCases.Add(casesList[i]);
                }
            }
        }
        #endregion

        #region Step 4 - Methods
        /// <summary>
        /// primary method of step 4
        /// -1. match bestCase with inputList and create solution
        ///  2. after this method is over - solution dictionary is set.
        /// </summary>


        private void MatchBestCasesWithAulist()
        {//sort by size then by typicality {size, [typicality,(list case) ] }
            SortedList<int, SortedList<int, List<Case>>> sortedList = new SortedList<int, SortedList<int, List<Case>>>(new DescendedsortedListComparer());
            Dictionary<Chunk, bool> isCaseCh = new Dictionary<Chunk, bool>();
            foreach (Case bestCase in BestCases)
            {
                SortedList<int, List<Case>> typicalityList; //value to save typicality list
                List<Case> caseList; //value to save case list
                if (sortedList.TryGetValue(bestCase.AuList.Count, out typicalityList) == false)//gets the typicality list made, from the size key
                {
                    sortedList.Add(bestCase.AuList.Count, new SortedList<int, List<Case>>(new DescendedsortedListComparer()));//add new typicality sorted list to size key
                    sortedList.TryGetValue(bestCase.AuList.Count, out typicalityList); //take the typicalityList made
                    if (typicalityList.TryGetValue(bestCase.Typicality, out caseList) == false)//gets the typicality list made, from the size key
                    {
                        typicalityList.Add(bestCase.Typicality, new List<Case>());//add new case List to this key of typicality
                        typicalityList.TryGetValue(bestCase.Typicality, out caseList); //take the caseList made
                        caseList.Add(bestCase);//adds the new Case to sorted list
                    }
                    else
                    {
                        caseList.Add(bestCase);//adds the new Case to sorted list
                    }
                }
                else
                {
                    if (typicalityList.TryGetValue(bestCase.Typicality, out caseList) == false)//gets the typicality list made, from the size key
                    {
                        typicalityList.Add(bestCase.Typicality, new List<Case>());//add new case List to this key of typicality
                        typicalityList.TryGetValue(bestCase.Typicality, out caseList); //take the caseList made
                        caseList.Add(bestCase);//adds the new Case to sorted list - unique
                    }
                    else
                    {
                        caseList.Add(bestCase);//adds the new Case to sorted list
                    }
                }
                typicalityList = null;
                caseList = null;
            }
            //descending walkthrough to find highest combination and after that highest typicality

            foreach (SortedList<int, List<Case>> sortedCount in sortedList.Values)
            {
                foreach (List<Case> caseList in sortedCount.Values)
                {
                    foreach (Case bestCase in caseList)
                    {
                        bool keepCase = true;
                        foreach (int au in bestCase.AuList)
                        {
                            // if at least one au dont apears in both case index and input au list
                            // discard case -> keepCase == false
                            if (savedAuList.Contains(au) == false)
                                keepCase = false;
                        }

                        if (keepCase == true)
                        { // remove all aus in inputAuList that appears in this case index
                            foreach (int au in bestCase.AuList)
                                savedAuList.Remove(au);
                            SolutionAddCase(bestCase, GetChunkFromChunkId(bestCase.ChunkID), isCaseCh);
                        }
                    }
                }
            }
        }


        private Chunk GetChunkFromChunkId(int chunkID)
        {

            foreach (Chunk chunk in chunks)
            {
                if (chunkID == chunk.ChunkId)
                    return chunk;

            }
            return null;
        }

        /// <summary>
        /// will do union Cases - will add to exist case in solution , all au's of given Case
        /// 
        /// for examlple
        /// 
        /// solution [Happy,Case-1] ,Case-1 has AuList [1,2]
        /// 
        /// new case is arrived with Aulist[6,11]
        /// 
        /// so Case-1 will now have combined auList [1,2,6,11]
        /// 
        /// </summary>
        /// <param name="Case"></param>
        /// <param name="chunk"></param>
        /// <param name="solutions"></param>
        private void SolutionAddCase(Case casei, Chunk chunk, Dictionary<Chunk, bool> isCaseCh)
        {
            if (solutions.ContainsKey(chunk) == false)
            {
                solutions.Add(chunk, casei);
                isCaseCh.Add(chunk, false);
                casei.ExistsInDb = true;
                casei.Typicality++;
            }
            else
            { 
                if (isCaseCh[chunk] == false)
                {
                    solutions[chunk].ExistsInDb = false;
                    solutions[chunk].Typicality = 1;
                    isCaseCh[chunk] = true;
                }
                foreach (int au in casei.AuList) //adds new aus to current case in current chunk
                {
                    solutions[chunk].AuList.Add(au);
                }
            }
        }
    }
        #endregion

    #region descending order comperator
    //descending order comperator
    class DescendedsortedListComparer : IComparer<int>
    {
        public int Compare(int x, int y)
        {
            // use the default comparer to do the original comparison for datetimes
            int ascendingResult = Comparer<int>.Default.Compare(x, y);

            // turn the result around
            return 0 - ascendingResult; //check if instead need to do (-1)*ascendingResult
        }
    }
}
    #endregion